<dialog>とPreactでmodal windowを作ってみる
背景
takker-workflow@0.0.1/next-action-viewerなどに使っているmodal windowは、scrapboxのmodal windowを真似て擬似的に作ったもの
標準機能である<dialog>を使って作り直したい
focus制御やクリック判定などに悩まされないで済む?
2023-08-23
17:50:08 custom hooksに切り出してみる
$ deno check -r=https://scrapbox.io --remote https://scrapbox.io/api/code/takker/%3Cdialog%3EとPreactでmodal_windowを作ってみる/app2.tsx
code:app2.tsx
/// <reference no-default-lib="true" />
/// <reference lib="esnext" />
/// <reference lib="dom" />
/** @jsx h */
/** @jsxFrag Fragment */
import { Fragment, h, render } from "../preact/mod.tsx";
import { useCallback, useState, useMemo, useEffect, useRef } from "../preact/hooks.ts";
const useDialog = () => {
const ref = useRef<HTMLDialogElement>(null);
const open = useCallback(() => ref.current?.showModal?.(), []);
const close = useCallback(() => ref.current?.close?.(), []);
return { ref, open, close, isOpen: ref.current?.open ?? false };
};
const App = () => {
const { ref, open, close } = useDialog();
/** dialogクリックではmodalを閉じないようにする */
const stopPropagation = useCallback((e: Event) => e.stopPropagation(), []);
return (<>
<style>{`
body:has(dialog:modal) {
touch-action: none;
overflow: hidden;
overscroll-behavior: none;
}
button.open {
position: fixed;
z-index: 1050;
top: 10%;
left: 10%;
}
dialog {
background: red;
}
`}</style>
<button className="open" onClick={open}>open modal</button>
<dialog ref={ref} onClick={close}>
<div className="container" onClick={stopPropagation}>
<p>dialog内のテキスト</p>
<button className="close" onClick={close}>close</button>
</div>
</dialog>
</>)
};
const app = document.createElement("div");
const shadowRoot = app.attachShadow({ mode: "open" });
document.body.append(app);
render(<App />, shadowRoot);
17:23:42 <dialog>表示時に背景コンテンツのスクロールをロックするために、:modalを使う
https://dev.classmethod.jp/articles/dialog-element-and-modal-pseudo-class/
:has()とも組み合わせる必要あり
code:css
body:has(dialog:modal) {
touch-action: none;
overflow: hidden;
overscroll-behavior: none;
}
-webkit-overflow-scrollingは廃止されたので不要
17:35:36 うーん、スクロールがロックされないぞ?
shadowDOMをやめても同じだった
$ deno check -r=https://scrapbox.io --remote https://scrapbox.io/api/code/takker/%3Cdialog%3EとPreactでmodal_windowを作ってみる/app.tsx
code:app.tsx
/// <reference no-default-lib="true" />
/// <reference lib="esnext" />
/// <reference lib="dom" />
/** @jsx h */
/** @jsxFrag Fragment */
import { Fragment, h, render } from "../preact/mod.tsx";
import { useCallback, useState, useMemo, useEffect, useRef } from "../preact/hooks.ts";
const App = () => {
const ref = useRef<HTMLDialogElement>(null);
const open = useCallback(() => ref.current?.showModal?.(), []);
const close = useCallback(() => ref.current?.close?.(), []);
/** dialogクリックではmodalを閉じないようにする */
const stopPropagation = useCallback((e: Event) => e.stopPropagation(), []);
return (<>
<style>{`
body:has(dialog:modal) {
touch-action: none;
overflow: hidden;
overscroll-behavior: none;
}
button.open {
position: fixed;
z-index: 1050;
top: 10%;
left: 10%;
}
`}</style>
<button className="open" onClick={open}>open modal</button>
<dialog ref={ref} onClick={close}>
<div className="container" onClick={stopPropagation}>
<p>dialog内のテキスト</p>
<button className="close" onClick={close}>close</button>
</div>
</dialog>
</>)
};
const app = document.createElement("div");
const shadowRoot = app.attachShadow({ mode: "open" });
document.body.append(app);
render(<App />, shadowRoot);
#2023-08-23 17:13:42